﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
using Newtonsoft.Json.Linq;
using TrinityAPI.Base;
using TrinityAPI.Base.Tools;
using TrinityAPI.Base.Models;
using DouYin.Open.Platform.Tools;

namespace DouYin.Open.Platform
{
    public class GetAccessToken : IAction
    {
        public JToken Invoke(JObject dataset, params JToken[] args)
        {
            try
            {
                //var query = dataset.GetJsonObject("query");
                //var constant = dataset.GetJsonObject("constant");
                //var douyinAPIConfig = constant.GetJsonObject("douyinAPIConfig");

                //var eshops = constant.GetJsonObject("eshops");
                //string url = douyinAPIConfig.GetJsonObjectStringValue("access_token_url");
                //string accessTokenDir = douyinAPIConfig.GetJsonObjectStringValue("access_token_dir");
                //string appKey = douyinAPIConfig.GetJsonObjectStringValue("app_key");
                //string appSecret = douyinAPIConfig.GetJsonObjectStringValue("app_secret");
                //string state = query.GetJsonObjectStringValue("state");
                //string code = query.GetJsonObjectStringValue("code");

                //if (eshops == null || eshops.Count == 0 || string.IsNullOrWhiteSpace(accessTokenDir) || string.IsNullOrWhiteSpace(url) || string.IsNullOrWhiteSpace(appKey) || string.IsNullOrWhiteSpace(appSecret))
                //    throw new BaseException("@1002", ""); //配置错误

                //if (string.IsNullOrWhiteSpace(state))
                //    throw new BaseException("@1003", ""); //参数错误

                //if (string.IsNullOrWhiteSpace(code))
                //    throw new BaseException("@1004", ""); //无法获得授权码

                //url = string.Format(url + "?app_key={0}&app_secret={1}&grant_type=authorization_code&code={2}", appKey, appSecret, code);
                //string responseString = HttpHelper.HttpWebRequest(url);
                //if (string.IsNullOrWhiteSpace(responseString))
                //    throw new BaseException("@1005", ""); //授权信息获取失败

                //var json = JSONHelper.JsonToJObject(responseString);
                //if (json == null || json.GetJsonObjectIntValue("code", 1) != 0)
                //{
                //    var error = APIContext.ErrorMessages.GetJsonObjectStringValue("1006");
                //    var msg = json.GetJsonObjectStringValue("msg");
                //    if (string.IsNullOrWhiteSpace(error) || string.IsNullOrWhiteSpace(msg))
                //        throw new BaseException("@1005", ""); //授权信息获取失败
                //    else
                //        throw new BaseException("1006", string.Format(error, msg));//授权信息获取失败(带平台错误信息）
                //}

                //int expires = json.GetJsonObjectIntValue("expires_in");
                //if (expires <= 0)
                //{
                //    var error = APIContext.ErrorMessages.GetJsonObjectStringValue("1006");
                //    throw new BaseException("1006", string.Format(error, "expired"));
                //}

                //json["expires_time"] = DateTime.Now.AddSeconds(expires);

                //string uid = json.GetJsonObjectStringValue("uid");
                //string eShopCode = eshops.GetJsonObjectStringValue(uid)?.ToLower();

                //if (string.IsNullOrWhiteSpace(eShopCode))
                //    throw new BaseException("@1007", ""); //实际授权用户和配置不匹配

                //SaveToken(Utils.GetMapPath(accessTokenDir), eShopCode, json.ToString(Newtonsoft.Json.Formatting.None));

                //if (eShopCode != state)
                //{
                //    var error = APIContext.ErrorMessages.GetJsonObjectStringValue("1008");
                //    throw new BaseException("1008", string.Format(error, eShopCode.ToUpper()));//授权账号不匹配
                //}
            }
            catch (BaseException ex)
            {
                if (!string.IsNullOrEmpty(ex.Message))
                    throw ex;
                throw new Exception(ex.Code);
            }
            catch (Exception ex)
            {
                LogHelper.Info("GetAccessToken fail: " + ex.Message);
                throw new Exception("@1001");
            }

            return null;
        }

        public static string GetToken(string eShopCode, JObject constant)
        {
            if (string.IsNullOrWhiteSpace(eShopCode))
                throw new ArgumentNullException("eShopCode");

            var douyinAPIConfig = constant.GetJsonObject("douyinAPIConfig");
            var eshopAPIConfig = douyinAPIConfig.GetJsonObject("eshops").GetJsonObject(eShopCode);
            int hours = douyinAPIConfig.GetJsonObjectIntValue("refresh_token_before_hours");
            string accessTokenDir = douyinAPIConfig.GetNecessaryJsonObjectStringValue("access_token_dir", "constant.douyinAPIConfig");

            if (hours <= 0)
                hours = 1;

            accessTokenDir = Utils.GetMapPath(accessTokenDir);
            var accessTokenInfo = JSONHelper.JsonToJObject(Utils.LoadTextFile(string.Format("{0}\\dy_access_token_{1}.config", accessTokenDir, eShopCode.ToLower())));
            if (accessTokenInfo == null)
            {
                try
                {
                    string url = douyinAPIConfig.GetNecessaryJsonObjectStringValue("url", "constant.douyinAPIConfig");
                    string appKey = eshopAPIConfig.GetNecessaryJsonObjectStringValue("app_key", "constant.douyinAPIConfig.eshops." + eShopCode);
                    string appSecret = eshopAPIConfig.GetNecessaryJsonObjectStringValue("app_secret", "constant.douyinAPIConfig.eshops." + eShopCode);
                    string shopid = eshopAPIConfig.GetNecessaryJsonObjectStringValue("shop_id", "constant.douyinAPIConfig.eshops." + eShopCode);
                    accessTokenInfo = CreateToken(accessTokenDir, eShopCode, shopid, url, appKey, appSecret);
                }
                catch (Exception ex)
                {
                    LogHelper.Error("create token error", ex);
                    var emailAlter = constant.GetJsonObject("emailAlter");
                    SendAlertMessage(emailAlter, eShopCode);
                    throw new BaseException("@0003", ex.Message); //授权失败
                }
            }
            else
            {
                var now = DateTime.Now;
                var expireTime = accessTokenInfo.GetJsonObjectDateTimeValue("expires_time", now);
                var expireHours = (expireTime - now).TotalHours;
                if (expireHours <= hours)
                {
                    try
                    {
                        string url = douyinAPIConfig.GetNecessaryJsonObjectStringValue("url", "constant.douyinAPIConfig");
                        string appKey = eshopAPIConfig.GetNecessaryJsonObjectStringValue("app_key", "constant.douyinAPIConfig.eshops." + eShopCode);
                        string appSecret = eshopAPIConfig.GetNecessaryJsonObjectStringValue("app_secret", "constant.douyinAPIConfig.eshops." + eShopCode);
                        string shopid = eshopAPIConfig.GetNecessaryJsonObjectStringValue("shop_id", "constant.douyinAPIConfig.eshops." + eShopCode);
                        string refreshToken = accessTokenInfo.GetJsonObjectStringValue("refresh_token");
                        string accessToken = accessTokenInfo.GetJsonObjectStringValue("access_token");
                        var refreshedTokenInfo = RefreshToken(accessTokenDir, eShopCode, shopid, url, appKey, appSecret, accessToken, refreshToken);
                        accessTokenInfo = refreshedTokenInfo;
                    }
                    catch (Exception ex)
                    {
                        LogHelper.Error("refresh token error", ex);
                        var emailAlter = constant.GetJsonObject("emailAlter");
                        SendAlertMessage(emailAlter, eShopCode, expireHours);
                        if (expireHours <= 0)
                            throw new BaseException("@0004", ex.Message); //授权已过期
                    }
                }
            }

            return accessTokenInfo.GetJsonObjectStringValue("access_token");
        }

        private static JObject CreateToken(string dir, string eShopCode, string shopid, string url, string appKey, string appSecret)
        {
            if (string.IsNullOrWhiteSpace(dir))
                throw new ArgumentNullException("dir");
            if (string.IsNullOrWhiteSpace(eShopCode))
                throw new ArgumentNullException("eShopCode");
            if (string.IsNullOrWhiteSpace(url))
                throw new ArgumentNullException("url");

            string method = "token.create";
            string version = "2";
            string timestamp = ApiUtils.GetTimeStamp(DateTime.Now).ToString();
            string param_json = "{\"code\":\"\",\"grant_type\":\"authorization_self\",\"shop_id\":\"" + shopid + "\"}";
            string sign = ApiUtils.Sign(appKey, appSecret, method, timestamp, version, param_json);
            string sign_method = "hmac-sha256";

            if (!url.EndsWith("/"))
                url = url + "/";
            string responseString = HttpHelper.HttpWebRequest(string.Format(url + "token/create?app_key={0}&method={1}&param_json={2}&timestamp={3}&v={4}&sign={5}&sign_method={6}", appKey, method, System.Web.HttpUtility.UrlEncode(param_json), timestamp, version, sign, sign_method), null, "POST");
            if (string.IsNullOrWhiteSpace(responseString))
                throw new Exception("create token no response");

            var json = JSONHelper.JsonToJObject(responseString);

            if (json == null)
                throw new Exception("create token no json response");

            if (json.GetJsonObjectIntValue("code", 1) != 10000)
            {
                var msg = json.GetJsonObjectStringValue("msg");
                throw new Exception(string.Format("create token code={0},msg={1}", json.GetJsonObjectValue("code")?.ToString(), msg));
            }

            var data = json.GetJsonObject("data");
            int expires = data.GetJsonObjectIntValue("expires_in");
            if (expires <= 0)
                throw new Exception("create token no 'expires_in' in json response");

            data["expires_time"] = DateTime.Now.AddSeconds(expires);

            SaveToken(dir, eShopCode, data.ToString(Newtonsoft.Json.Formatting.None));

            return data;
        }

        private static JObject RefreshToken(string dir, string eShopCode, string shopid, string url, string appKey, string appSecret, string accessToken, string refreshToken)
        {
            if (string.IsNullOrWhiteSpace(dir))
                throw new ArgumentNullException("dir");
            if (string.IsNullOrWhiteSpace(eShopCode))
                throw new ArgumentNullException("eShopCode");
            if (string.IsNullOrWhiteSpace(url))
                throw new ArgumentNullException("url");

            string method = "token.refresh";
            string version = "2";
            string timestamp = ApiUtils.GetTimeStamp(DateTime.Now).ToString();
            string param_json = "{\"grant_type\":\"refresh_token\",\"refresh_token\":\"" + refreshToken + "\"}";
            string sign = ApiUtils.Sign(appKey, appSecret, method, timestamp, version, param_json);
            string sign_method = "hmac-sha256";

            if (!url.EndsWith("/"))
                url = url + "/";
            string responseString = HttpHelper.HttpWebRequest(string.Format(url + "token/refresh?app_key={0}&method={1}&access_token={2}&param_json={3}&timestamp={4}&v={5}&sign={6}&sign_method={7}", appKey, method, accessToken, System.Web.HttpUtility.UrlEncode(param_json), timestamp, version, sign, sign_method), null, "POST");
            if (string.IsNullOrWhiteSpace(responseString))
                throw new Exception("refresh token no response");

            var json = JSONHelper.JsonToJObject(responseString);

            if (json == null)
                throw new Exception("refresh token no json response");

            if (json.GetJsonObjectIntValue("code", 1) != 10000)
            {
                var msg = json.GetJsonObjectStringValue("msg");
                throw new Exception(string.Format("refresh token code={0},msg={1}", json.GetJsonObjectValue("code")?.ToString(), msg));
            }

            var data = json.GetJsonObject("data");
            int expires = data.GetJsonObjectIntValue("expires_in");
            if (expires <= 0)
                throw new Exception("refresh token no 'expires_in' in json response");

            data["expires_time"] = DateTime.Now.AddSeconds(expires);

            SaveToken(dir, eShopCode, data.ToString(Newtonsoft.Json.Formatting.None));

            return data;
        }

        private static void SaveToken(string dir, string eShopCode, string content)
        {
            if (string.IsNullOrWhiteSpace(dir))
                throw new ArgumentNullException("dir");
            if (string.IsNullOrWhiteSpace(eShopCode))
                throw new ArgumentNullException("eShopCode");
            if (string.IsNullOrWhiteSpace(content))
                throw new ArgumentNullException("content");

            FileStream fileStream = null;
            StreamWriter streamWriter = null;
            try
            {
                if (!Directory.Exists(dir))
                {
                    Directory.CreateDirectory(dir);
                }

                fileStream = new FileStream(string.Format("{0}\\dy_access_token_{1}.config", dir, eShopCode.ToLower()), FileMode.Create);
                streamWriter = new StreamWriter(fileStream, Encoding.Default);
                streamWriter.Write(content);
                streamWriter.Flush();
                streamWriter.Close();
                streamWriter.Dispose();
                fileStream.Close();
                fileStream.Dispose();
                streamWriter = null;
                fileStream = null;
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                if (streamWriter != null)
                {
                    streamWriter.Flush();
                    streamWriter.Close();
                    streamWriter.Dispose();
                }
                if (fileStream != null)
                {
                    fileStream.Close();
                    fileStream.Dispose();
                }
            }
        }

        private static void SendAlertMessage(JObject config, string eShopCode, double? hours = null)
        {
            if (config == null || string.IsNullOrWhiteSpace(eShopCode))
                return;
            string url = config.GetJsonObjectStringValue("url");
            string apiKey = config.GetJsonObjectStringValue("apiKey");
            string secretKey = config.GetJsonObjectStringValue("secretKey");
            string receiver = config.GetJsonObjectStringValue("receiver");
            string cc = config.GetJsonObjectStringValue("cc");
            string bcc = config.GetJsonObjectStringValue("bcc");
            if (string.IsNullOrWhiteSpace(url) || string.IsNullOrWhiteSpace(apiKey) || string.IsNullOrWhiteSpace(secretKey) || string.IsNullOrWhiteSpace(receiver))
                return;
            System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(o =>
            {
                try
                {
                    var eShopName = eShopCode.ToUpper();
                    var requestTime = DateTime.UtcNow.ToString("yyyyMMddHHmmss");

                    var md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
                    byte[] result = md5.ComputeHash(Encoding.UTF8.GetBytes(apiKey + requestTime + secretKey));

                    var sb = new StringBuilder();

                    for (int i = 0; i < result.Length; i++)
                    {
                        sb.Append(result[i].ToString("X2"));
                    }
                    string authKey = sb.ToString();

                    string subject = string.Format("{0} 抖音开放平台自动授权失败", eShopName);
                    string message = string.Format("{0} 抖音开放平台自动授权失败。请处理：", eShopName);

                    if (hours.HasValue)
                    {
                        string flag = hours > 0 ? "将在" + DateTime.Now.AddHours(hours.Value).ToString("yyyy-MM-dd HH:mm") : "已经";
                        subject = string.Format("{0} 抖音开放平台授权{1}过期，但自动授权失败", eShopName, hours.Value > 0 ? "即将" : "已经");
                        message = string.Format("{0} 抖音开放平台授权{1}过期，但自动授权失败。请处理：", eShopName, flag);
                    }

                    string body = "<style>/*TableHead*/#table-5theadth{background-color:rgb(156,186,95);color:#fff;border-bottom-width:0;}/*HeadingandColumnStyle*/#table-5tr,#table-5th,#table-5td{border-width:1px;border-style:solid;border-color:rgb(156,186,95);}/*Paddingandfontstyle*/#table-5th{padding:5px10px;font-size:12px;font-family:Verdana;font-weight:bold;}#table-5td{padding:5px10px;}#table-5tabletd{white-space:nowrap;font-size:12px;font-family:Verdana;border:none;}body{font-size:15px;font-family:Verdana;font-weight:100;}</style><body>Dear Team:<br/><br/>" + message + "<br/><br/>(这是系统生成的电子邮件，请不要回复。)<br/><br/></body>";

                    var request = new JObject();
                    request.Add("apiKey", apiKey);
                    request.Add("authKey", authKey);
                    request.Add("requestTime", requestTime);
                    request.Add("subject", subject);
                    request.Add("message", body);
                    request.Add("receiver", receiver);
                    request.Add("cc", cc);
                    request.Add("bcc", bcc);

                    var requestString = request.ToString(Newtonsoft.Json.Formatting.None);

                    HttpHelper.HttpWebRequest(url, requestString, "POST", 10000, new Dictionary<string, string>() { { "Content-Type", "application/json" } });
                }
                catch (Exception ex)
                {
                    LogHelper.Error("Send authorization email error", ex);
                }
            }));
        }
    }
}
